package org.tulkas.ext;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.tulkas.base.Constants;
import org.tulkas.base.ITulkasApplicationContext;
import org.tulkas.exception.ParseException;
import org.tulkas.util.XmlDomUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 * @author <a href="mailto:tulkas.web@gmail.com">Tulkas</a>
 *
 */
@Service
public class PluginManager {
	private static Logger logger = LoggerFactory.getLogger(PluginManager.class);

	@Autowired
	private ITulkasApplicationContext appContext;
	private Map<String, Plugin> plugins = new HashMap<String, Plugin>();

	public URL[] searchFromPlugins(String filePath) {
		List<URL> urls = new ArrayList<URL>();
		try {
			Enumeration<URL> em = appContext.getClassLoader().getResources(
					filePath);
			while (em.hasMoreElements()) {
				urls.add(em.nextElement());
			}
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
		}

		File pluginDir = new File(appContext.getRootPath() + "/"
				+ Constants.PLUGINS_DIR);

		File[] files = pluginDir.listFiles();
		for (File dir : files) {
			if (dir.isDirectory()) {
				String pluginFileName = dir.getAbsolutePath() + "/" + filePath;
				File pluginFile = new File(pluginFileName);
				if (pluginFile.exists()) {
					try {
						urls.add(pluginFile.toURI().toURL());
					} catch (MalformedURLException e) {
						logger.error(e.getMessage(), e);
					}
				}
			}
		}

		return urls.toArray(new URL[urls.size()]);
	}

	public void init() {
		URL[] pluginConfigs = searchFromPlugins(Constants.PLUGIN_FILE_NAME);
		for (URL pluginConfig : pluginConfigs) {
			try {
				InputStream is = pluginConfig.openStream();
				Document doc = XmlDomUtil.loadFromStream(is);
				is.close();
				Plugin plugin = parsePlugin(doc);
				plugins.put(plugin.getId(), plugin);
			} catch (Exception e) {
				logger.error("Error loading plugin configuration [location: "
						+ pluginConfig.toString() + "]", e);
			}
		}
	}

	public static Plugin parsePlugin(Document doc) throws ParseException {
		Plugin plugin = new Plugin();
		Element rootEle = doc.getDocumentElement();
		String id = rootEle.getAttribute("id");
		Assert.notNull(id, "plugin id must not be null");
		plugin.setId(id);
		plugin.setVersion(rootEle.getAttribute("version"));
		plugin
				.setName(XmlDomUtil.getStringFromFirstSubElement(rootEle,
						"name"));
		plugin.setDescription(XmlDomUtil.getStringFromFirstSubElement(rootEle,
				"description"));
		plugin.setProvider(XmlDomUtil.getStringFromFirstSubElement(rootEle,
				"provider"));
		plugin
				.setIcon(XmlDomUtil.getStringFromFirstSubElement(rootEle,
						"icon"));
		Element deps = XmlDomUtil.getFirstSubElement(rootEle, "dependencies");
		if (deps != null) {
			Element[] eles = XmlDomUtil.getSubElements(deps, "dependency");
			for (Element ele : eles) {
				plugin.addDependency(ele.getAttribute("plugin-id"), ele
						.getAttribute("version"));
			}
		}

		Element confExts = XmlDomUtil.getFirstSubElement(rootEle, "extensions");
		ConfigExtension[] exts = null;
		if (confExts != null) {
			Element[] eles = XmlDomUtil.getSubElements(confExts, "extension");
			exts = new ConfigExtension[eles.length];
			for (int i = 0; i < eles.length; i++) {
				Element extEle = eles[i];
				exts[i] = new ConfigExtension();
				exts[i].setPluginId(id);
				String pointId = extEle.getAttribute("point-id");
				Assert.notNull(pointId, "pointId must not be null");
				exts[i].setPointId(pointId);
				exts[i].setName(XmlDomUtil.getStringFromFirstSubElement(extEle,
						"name"));
				exts[i].setDescription(XmlDomUtil.getStringFromFirstSubElement(
						extEle, "description"));
				Element paramsEle = XmlDomUtil.getFirstSubElement(extEle,
						"params");
				if (paramsEle != null) {
					Element[] paramEles = XmlDomUtil.getSubElements(paramsEle,
							"param");
					for (Element paramEle : paramEles) {
						String name = paramEle.getAttribute("name");
						Assert.notNull(name, "name must not be null");
						String value = paramEle.getTextContent();
						exts[i].setParam(name, value);
					}
				}
			}
		} else {
			exts = new ConfigExtension[0];
		}
		plugin.setConfigExtensions(exts);

		return plugin;
	}

	public Plugin[] getAllPlugins() {
		return plugins.values().toArray(new Plugin[plugins.size()]);
	}

	public Plugin getPluginById(String pluginId) {
		return plugins.get(pluginId);
	}
}
